package devenv

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"sync"
	"time"

	"github.com/louloulin/dataflare/pkg/workflow/connector"
)

// DebugPoint represents a debug point in a workflow
type DebugPoint struct {
	// Type is the type of debug point
	Type string `json:"type"`
	// ComponentID is the ID of the component
	ComponentID string `json:"component_id"`
	// Condition is the condition for the debug point
	Condition string `json:"condition"`
	// Enabled indicates if the debug point is enabled
	Enabled bool `json:"enabled"`
}

// DebugSession represents a debug session
type DebugSession struct {
	// runtime is the local runtime
	runtime *LocalRuntime
	// debugPoints is a map of debug points
	debugPoints map[string]*DebugPoint
	// recordSnapshots is a map of record snapshots
	recordSnapshots map[string][]*connector.Record
	// componentMetrics is a map of component metrics
	componentMetrics map[string]map[string]interface{}
	// mu is the mutex
	mu sync.RWMutex
	// logger is the logger
	logger *log.Logger
	// workflowName is the name of the workflow being debugged
	workflowName string
	// runID is the ID of the workflow run
	runID string
	// ctx is the context
	ctx context.Context
	// cancel is the context cancel function
	cancel context.CancelFunc
	// snapshotInterval is the interval for taking snapshots
	snapshotInterval time.Duration
}

// NewDebugSession creates a new debug session
func NewDebugSession(runtime *LocalRuntime, workflowName string) (*DebugSession, error) {
	// Create a context with cancel
	ctx, cancel := context.WithCancel(context.Background())

	// Create a debug session
	session := &DebugSession{
		runtime:          runtime,
		debugPoints:      make(map[string]*DebugPoint),
		recordSnapshots:  make(map[string][]*connector.Record),
		componentMetrics: make(map[string]map[string]interface{}),
		ctx:              ctx,
		cancel:           cancel,
		logger:           log.New(os.Stdout, "[DebugSession] ", log.LstdFlags),
		workflowName:     workflowName,
		snapshotInterval: 5 * time.Second,
	}

	return session, nil
}

// Start starts the debug session
func (s *DebugSession) Start() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Start the workflow
	runID, err := s.runtime.StartWorkflow(s.workflowName, nil)
	if err != nil {
		return fmt.Errorf("failed to start workflow: %w", err)
	}
	s.runID = runID

	// Start snapshot collection
	go s.collectSnapshots()

	s.logger.Printf("Started debug session for workflow %s with run ID %s", s.workflowName, runID)

	return nil
}

// Stop stops the debug session
func (s *DebugSession) Stop() error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Cancel context
	s.cancel()

	// Stop the workflow
	if err := s.runtime.StopWorkflow(s.workflowName); err != nil {
		return fmt.Errorf("failed to stop workflow: %w", err)
	}

	s.logger.Printf("Stopped debug session for workflow %s", s.workflowName)

	return nil
}

// AddDebugPoint adds a debug point
func (s *DebugSession) AddDebugPoint(debugPoint *DebugPoint) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Add debug point
	s.debugPoints[debugPoint.ComponentID] = debugPoint

	s.logger.Printf("Added debug point for component %s", debugPoint.ComponentID)

	return nil
}

// RemoveDebugPoint removes a debug point
func (s *DebugSession) RemoveDebugPoint(componentID string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Remove debug point
	delete(s.debugPoints, componentID)

	s.logger.Printf("Removed debug point for component %s", componentID)

	return nil
}

// GetRecordSnapshots gets record snapshots for a component
func (s *DebugSession) GetRecordSnapshots(componentID string) ([]*connector.Record, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// Get record snapshots
	snapshots, ok := s.recordSnapshots[componentID]
	if !ok {
		return nil, fmt.Errorf("no record snapshots for component %s", componentID)
	}

	return snapshots, nil
}

// GetComponentMetrics gets metrics for a component
func (s *DebugSession) GetComponentMetrics(componentID string) (map[string]interface{}, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// Get component metrics
	metrics, ok := s.componentMetrics[componentID]
	if !ok {
		return nil, fmt.Errorf("no metrics for component %s", componentID)
	}

	return metrics, nil
}

// collectSnapshots collects snapshots of records and metrics
func (s *DebugSession) collectSnapshots() {
	ticker := time.NewTicker(s.snapshotInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			// Collect snapshots
			s.collectRecordSnapshots()
			s.collectComponentMetrics()
		case <-s.ctx.Done():
			return
		}
	}
}

// collectRecordSnapshots collects snapshots of records
func (s *DebugSession) collectRecordSnapshots() {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Get workflow ID
	workflowID, ok := s.runtime.DeployedWorkflows[s.workflowName]
	if !ok {
		s.logger.Printf("Workflow %s not deployed", s.workflowName)
		return
	}

	// Get workflow status
	status, err := s.runtime.engine.GetWorkflowStatus(s.ctx, s.runID)
	if err != nil {
		s.logger.Printf("Failed to get workflow status: %v", err)
		return
	}

	// Check if workflow is running
	if status.Status != "running" {
		return
	}

	// Get workflow metrics
	metrics, err := s.runtime.engine.GetWorkflowMetrics(s.ctx, workflowID, s.runID)
	if err != nil {
		s.logger.Printf("Failed to get workflow metrics: %v", err)
		return
	}

	// Extract component metrics
	for componentID, componentMetrics := range metrics {
		// Check if we have a debug point for this component
		debugPoint, ok := s.debugPoints[componentID]
		if !ok || !debugPoint.Enabled {
			continue
		}

		// Get record snapshots
		if recordsJSON, ok := componentMetrics["last_records"].(string); ok {
			var records []*connector.Record
			if err := json.Unmarshal([]byte(recordsJSON), &records); err != nil {
				s.logger.Printf("Failed to parse record snapshots: %v", err)
				continue
			}

			// Store record snapshots
			s.recordSnapshots[componentID] = records
		}
	}
}

// collectComponentMetrics collects metrics for components
func (s *DebugSession) collectComponentMetrics() {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Get workflow ID
	workflowID, ok := s.runtime.DeployedWorkflows[s.workflowName]
	if !ok {
		s.logger.Printf("Workflow %s not deployed", s.workflowName)
		return
	}

	// Get workflow status
	status, err := s.runtime.engine.GetWorkflowStatus(s.ctx, s.runID)
	if err != nil {
		s.logger.Printf("Failed to get workflow status: %v", err)
		return
	}

	// Check if workflow is running
	if status.Status != "running" {
		return
	}

	// Get workflow metrics
	metrics, err := s.runtime.engine.GetWorkflowMetrics(s.ctx, workflowID, s.runID)
	if err != nil {
		s.logger.Printf("Failed to get workflow metrics: %v", err)
		return
	}

	// Extract component metrics
	for componentID, componentMetrics := range metrics {
		// Store component metrics
		s.componentMetrics[componentID] = componentMetrics.(map[string]interface{})
	}
}

// ExportDebugData exports debug data to a file
func (s *DebugSession) ExportDebugData(filePath string) error {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// Create debug data
	debugData := map[string]interface{}{
		"workflow_name":     s.workflowName,
		"run_id":            s.runID,
		"debug_points":      s.debugPoints,
		"record_snapshots":  s.recordSnapshots,
		"component_metrics": s.componentMetrics,
		"timestamp":         time.Now().Format(time.RFC3339),
	}

	// Marshal debug data
	data, err := json.MarshalIndent(debugData, "", "  ")
	if err != nil {
		return fmt.Errorf("failed to marshal debug data: %w", err)
	}

	// Write debug data to file
	if err := os.WriteFile(filePath, data, 0644); err != nil {
		return fmt.Errorf("failed to write debug data: %w", err)
	}

	s.logger.Printf("Exported debug data to %s", filePath)

	return nil
}

// ImportDebugData imports debug data from a file
func (s *DebugSession) ImportDebugData(filePath string) error {
	s.mu.Lock()
	defer s.mu.Unlock()

	// Read debug data from file
	data, err := os.ReadFile(filePath)
	if err != nil {
		return fmt.Errorf("failed to read debug data: %w", err)
	}

	// Unmarshal debug data
	var debugData map[string]interface{}
	if err := json.Unmarshal(data, &debugData); err != nil {
		return fmt.Errorf("failed to unmarshal debug data: %w", err)
	}

	// Extract debug points
	if debugPointsData, ok := debugData["debug_points"].(map[string]interface{}); ok {
		for componentID, debugPointData := range debugPointsData {
			if debugPointMap, ok := debugPointData.(map[string]interface{}); ok {
				debugPoint := &DebugPoint{
					ComponentID: componentID,
				}
				if typ, ok := debugPointMap["type"].(string); ok {
					debugPoint.Type = typ
				}
				if condition, ok := debugPointMap["condition"].(string); ok {
					debugPoint.Condition = condition
				}
				if enabled, ok := debugPointMap["enabled"].(bool); ok {
					debugPoint.Enabled = enabled
				}
				s.debugPoints[componentID] = debugPoint
			}
		}
	}

	s.logger.Printf("Imported debug data from %s", filePath)

	return nil
}
